/**
 * Form 组件 - 表单容器组件
 *
 * 功能概述：
 * 1. 提供表单状态管理，包括字段值、验证状态、错误信息等
 * 2. 支持数据验证，基于 async-validator 库实现
 * 3. 处理表单提交逻辑，包括验证和回调触发
 * 4. 使用 React Context API 向下传递表单状态和方法给 FormItem 组件
 * 5. 支持函数式渲染子组件，可以获取表单状态进行动态渲染
 *
 * 设计模式：
 * - 使用 React Context 实现状态共享
 * - 使用 forwardRef 暴露组件方法给外部
 * - 使用自定义 Hook (useStore) 进行状态管理
 */

import React, {
  ReactNode,
  createContext,
  forwardRef,
  useImperativeHandle,
} from "react";
import { ValidateError } from "async-validator";
import useStore, { FormState } from "./useStore";

/**
 * 渲染函数类型，用于函数式子组件
 * 允许子组件接收表单状态作为参数进行动态渲染
 */
export type RenderProps = (form: FormState) => ReactNode;

/**
 * 基础 Form 组件属性接口
 * 定义了 Form 组件接收的所有配置选项
 */
export interface BaseFormProps {
  /** 表单名称，会作为表单字段 id 前缀使用，便于样式和测试 */
  name?: string;
  /** 表单默认值，只在组件初始化以及重置时生效 */
  initialValues?: Record<string, any>;
  /**
   * 子组件，可以是：
   * 1. ReactNode - 普通子组件
   * 2. RenderProps - 函数式子组件，接收表单状态
   */
  children?: ReactNode | RenderProps;
  /** 提交表单且数据验证成功后回调事件 */
  onFinish?: (values: Record<string, any>) => void;
  /** 提交表单且数据验证失败后回调事件 */
  onFinishFailed?: (
    values: Record<string, any>,
    errors: Record<string, ValidateError[]>
  ) => void;
  /** 表单布局方式 */
  layout?: "horizontal" | "vertical";
  /** 标签宽度 */
  labelWidth?: number;
  /** 标签对齐方式 */
  labelAlign?: "left" | "right";
}

/**
 * Form 组件属性接口
 * 继承基础属性并混入 HTML form 元素属性
 */
export type FormProps = BaseFormProps &
  Omit<React.FormHTMLAttributes<HTMLFormElement>, "onSubmit" | "children">;

/**
 * Form Context 类型定义
 * 包含表单状态管理相关方法和初始值，通过 Context 传递给子组件
 */
export type IFormContext = Pick<
  ReturnType<typeof useStore>,
  "dispatch" | "fields" | "validateField"
> &
  Pick<BaseFormProps, "initialValues" | "layout" | "labelWidth" | "labelAlign">;

/**
 * Form 组件引用类型
 * 暴露给外部的方法，通过 ref 可以调用这些方法
 * 排除了内部状态字段，只暴露操作方法
 */
export type IFormRef = Omit<
  ReturnType<typeof useStore>,
  "fields" | "dispatch" | "form"
>;

/**
 * 创建 Form Context
 * 用于在 Form 和 FormItem 组件之间共享状态和方法
 * 初始值为空对象，类型断言为 IFormContext
 */
export const FormContext = createContext<IFormContext>({} as IFormContext);

/**
 * Form 组件主函数
 *
 * 实现细节：
 * 1. 使用 forwardRef 包装，支持通过 ref 引用组件实例
 * 2. 使用 useStore Hook 管理表单状态
 * 3. 使用 useImperativeHandle 暴露方法给外部
 * 4. 使用 Context.Provider 向下传递状态和方法
 *
 * @param props - Form 组件属性
 * @param ref - React ref 引用
 * @returns Form 组件 JSX
 */
export const Form = forwardRef<IFormRef, FormProps>((props, ref) => {
  // 解构 props，获取表单配置，为 name 设置默认值
  const {
    name = "viking_form",
    children,
    initialValues,
    onFinish,
    onFinishFailed,
    layout = "vertical",
    labelWidth,
    labelAlign = "left",
    className,
    style,
    ...restFormProps
  } = props;

  /**
   * 使用自定义 Hook 管理表单状态
   * useStore 返回表单状态和操作方法
   */
  const { form, fields, dispatch, ...restProps } = useStore(initialValues);
  const { validateField, validateAllFields } = restProps;

  /**
   * 使用 useImperativeHandle 暴露表单方法给外部
   * 这样外部可以通过 ref 调用表单的方法，如 validateAllFields、resetFields 等
   */
  useImperativeHandle(ref, () => {
    return {
      ...restProps, // 暴露所有操作方法，排除内部状态字段
    };
  });

  /**
   * 准备传递给 Context 的数据
   * 包含 FormItem 组件需要的状态和方法
   */
  const passedContext: IFormContext = {
    dispatch, // 状态更新方法
    fields, // 所有字段状态
    initialValues, // 初始值
    validateField, // 字段验证方法
    layout, // 表单布局方式
    labelWidth, // 标签宽度
    labelAlign, // 标签对齐方式
  };

  /**
   * 表单提交处理函数
   *
   * 处理流程：
   * 1. 阻止默认提交行为
   * 2. 验证所有表单字段
   * 3. 根据验证结果调用相应的回调函数
   *
   * @param e - 表单提交事件
   */
  const submitForm = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault(); // 阻止默认表单提交
    e.stopPropagation(); // 阻止事件冒泡

    // 验证所有表单字段，返回验证结果
    const { isValid, errors, values } = await validateAllFields();

    // 根据验证结果调用相应回调
    if (isValid && onFinish) {
      // 验证成功，调用 onFinish 回调
      onFinish(values);
    } else if (!isValid && onFinishFailed) {
      // 验证失败，调用 onFinishFailed 回调
      onFinishFailed(values, errors);
    }
  };

  /**
   * 处理子组件渲染
   * 支持两种子组件形式：普通 ReactNode 和函数式渲染
   */
  let childrenNode: ReactNode;
  if (typeof children === "function") {
    // 如果 children 是函数，传入表单状态进行渲染
    // 这种模式允许子组件根据表单状态进行动态渲染
    childrenNode = children(form);
  } else {
    // 普通 React 子组件，直接渲染
    childrenNode = children;
  }

  /**
   * 返回表单 JSX
   * 使用 form 元素包裹，设置 onSubmit 事件处理器
   * 使用 Context.Provider 向下传递状态和方法
   */
  return (
    <form
      name={name}
      className={className ? `viking-form ${className}` : "viking-form"}
      style={style}
      onSubmit={submitForm} // 绑定提交事件处理器
      {...restFormProps} // 传递其他 HTML form 属性
    >
      {/*
       * 使用 Context Provider 向下传递表单状态和方法
       * FormItem 组件通过 useContext 获取这些数据
       */}
      <FormContext.Provider value={passedContext}>
        {childrenNode}
      </FormContext.Provider>
    </form>
  );
});

export default Form;
